using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using TestcraftLibrary;
using TestcraftLibrary.CVar;
using TestcraftLibrary.Network;
using TestcraftLibrary.Common;

namespace Testcraft
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class TestCraftGame : Microsoft.Xna.Framework.Game
    {
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        static extern void OutputDebugString(string lpOutputString);

        System.Diagnostics.Process process;

        private bool wireFrame = false;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        FirstPersonCamera camera;
        Player player;
        BasicEffect effect;
        SpriteFont font;

        Physics.PhysicsBody body1;
        Physics.PhysicsEngine engine;
        long timePhysics;
        long timeOctreeRebuild;
        long timeRender;
        Chunk[,] chunks;
        ChunkEngine chunkEngine;

        public CVarSystem CVarSystem { get; private set; }
        public NetworkHandler ServerConnection { get; set; }

        public TestCraftGame()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.SynchronizeWithVerticalRetrace = false;
            this.IsFixedTimeStep = false;
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredBackBufferWidth = 800;
            //graphics.PreferredBackBufferHeight = 1080;
            //graphics.PreferredBackBufferWidth = 1920;
            graphics.ApplyChanges();
            //graphics.PreferMultiSampling = true;
            //graphics.ToggleFullScreen();
            this.CVarSystem = new CVarSystem();


            this.process = System.Diagnostics.Process.GetCurrentProcess();
        }
        int foofoo = 0;
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            //System.Threading.ThreadPool.SetMaxThreads(1, 1000);
            this.CVarSystem.Startup();
            this.chunkEngine = new ChunkEngine(this);

            Lidgren.Network.NetPeerConfiguration netconfig = new Lidgren.Network.NetPeerConfiguration("testcraft");

            Lidgren.Network.NetClient client = new Lidgren.Network.NetClient(netconfig);
            client.Start();
            client.Connect("localhost", 34545);
            this.ServerConnection = new NetworkHandler(client);

            if (this.CVarSystem.Get("fullscreen") == null)
                this.CVarSystem.Set(new TestcraftLibrary.CVar.CVar("fullscreen", "Specifies if the game runs in full screen", false, true));

            SetFullscreen(this.CVarSystem.Get("fullscreen").GetBool());

            effect = new BasicEffect(GraphicsDevice);

            body1 = new Physics.PhysicsBody()
            {
                InRest = true,
                Bounds = new BoundingBox(new Vector3(-0.5f), new Vector3(0.5f)),
                IsStatic = false,
                Position = new Vector3(-20, 10, 20),
                Weight = 10,
                Velocity = Vector3.Zero
            };

            body1.Bounds = new BoundingBox(body1.Bounds.Min + body1.Position, body1.Bounds.Max + body1.Position);

            engine = new Physics.PhysicsEngine();
            Scene.Octree<Testcraft.Physics.PhysicsBody> octree = new Scene.Octree<Physics.PhysicsBody>(4);

            engine.Bodies.Add(body1);

            Random rand = new Random();

            octree.Build();



            engine.Scene = octree;

            this.IsMouseVisible = true;
            // TODO: Add your initialization logic here
            player = new Player();
            chunkEngine.Player = player;
            ServerConfiguration config = new ServerConfiguration();
            //config.ChunkRadius = 70;
            config.ChunkIndexPath = @"C:\projects\chunks.bin";
            //ServerChunkManager manager = new ServerChunkManager(config);
            Player p = new Player();
            //manager.LoadPlayerChunks(p);
            base.Initialize();
        }

        private void SetFullscreen(bool fullscreen)
        {
            if (fullscreen)
            {
                graphics.PreferredBackBufferHeight = 1080;
                graphics.PreferredBackBufferWidth = 1920;
                graphics.IsFullScreen = fullscreen;
            }
            else
            {
                graphics.PreferredBackBufferHeight = 600;
                graphics.PreferredBackBufferWidth = 800;
                graphics.IsFullScreen = fullscreen;
            }
            graphics.ApplyChanges();
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            this.chunkEngine.Shutdown();
            this.CVarSystem.Shutdown();
            base.OnExiting(sender, args);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            this.font = Content.Load<SpriteFont>("SpriteFont1");
            this.camera = new FirstPersonCamera(this);
            this.camera.Position = new Vector3(0, 23, 160);
            this.camera.Target = new Vector3(0, 23, 0);
            this.camera.Up = Vector3.Up;
            this.camera.FieldOfView = MathHelper.PiOver4;
            this.camera.AspectRatio = this.GraphicsDevice.Viewport.AspectRatio;
            this.camera.NearPlane = 0.1f;
            this.camera.FarPlane = ChunkEngine.ChunkRadius * Chunk.ChunkWidth * 0.5f;

            this.camera.mouseResetPosition = new Point(this.GraphicsDevice.Viewport.Width / 2, this.GraphicsDevice.Viewport.Height / 2);

            player.Position = this.body1.Position;
            player.Camera = this.camera;
            // TODO: use this.Content to load your game content here
            this.chunkEngine.Start();
            //this.chunkEngine.RunUpdateCycle();
        }
        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        Ray mouseRay;
        private int CompareChunks(ChunkPartEx2 x, ChunkPartEx2 y)
        {
            if (y == null && x != null)
                return 1;
            else if (y != null && x == null)
                return -1;
            else if (y == null && x == null)
                return 0;

            BoundingBox xBounds = new BoundingBox(new Vector3(x.Parent.X * Chunk.ChunkWidth, x.Index * 16, x.Parent.Y * Chunk.ChunkWidth),
                new Vector3(x.Parent.X * Chunk.ChunkWidth, x.Index * 16, x.Parent.Y * Chunk.ChunkWidth) + new Vector3(Chunk.ChunkWidth, 16, Chunk.ChunkWidth));

            BoundingBox yBounds = new BoundingBox(new Vector3(y.Parent.X * Chunk.ChunkWidth, y.Index * 16, y.Parent.Y * Chunk.ChunkWidth),
                new Vector3(y.Parent.X * Chunk.ChunkWidth, y.Index * 16, y.Parent.Y * Chunk.ChunkWidth) + new Vector3(Chunk.ChunkWidth, 16, Chunk.ChunkWidth));

            float? xDistance = mouseRay.Intersects(xBounds);
            float? yDistance = mouseRay.Intersects(yBounds);

            if (yDistance == null && xDistance != null)
                return 1;
            else if (yDistance != null && xDistance == null)
                return -1;
            else if (yDistance == null && xDistance == null)
                return 0;
            else
                return xDistance.Value.CompareTo(yDistance.Value);
        }

        KeyboardState oldState;
        MouseState oldMouseState;
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Player input
            KeyboardState keyState = Keyboard.GetState();
            Vector3 force = Vector3.Zero;
            if (keyState.IsKeyDown(Keys.W))
            {
                force += Vector3.UnitY;
            }
            if (keyState.IsKeyDown(Keys.S))
            {
                force -= Vector3.UnitY;
            }
            if (keyState.IsKeyDown(Keys.A))
            {
                force -= Vector3.UnitX;
            }
            if (keyState.IsKeyDown(Keys.D))
            {
                force += Vector3.UnitX;
            }
            if (keyState.IsKeyDown(Keys.Up))
            {
                force += Vector3.UnitZ;
            }
            if (keyState.IsKeyDown(Keys.Down))
            {
                force += -Vector3.UnitZ;
            }
            if (keyState.IsKeyDown(Keys.R))
            {
                this.body1.Velocity = Vector3.Zero;
                this.body1.Position = new Vector3(this.body1.Position.X, 10, this.body1.Position.Z);
            }
            if (keyState.IsKeyDown(Keys.I) && oldState.IsKeyUp(Keys.I))
                this.effect.FogEnabled = !this.effect.FogEnabled;
            if (keyState.IsKeyDown(Keys.B) && oldState.IsKeyUp(Keys.B))
            {
                using (System.IO.StreamWriter writer = new System.IO.StreamWriter(@"C:\Projects\dump.txt"))
                {
                    writer.WriteLine("{0} Loaded chunks", this.chunkEngine.loadedChunks.Count);
                    int activeChunkParts = 0;
                    int buffers = 0;
                    long vertices = 0;
                    for (int i = 0; i < this.chunkEngine.loadedChunks.Count; i++)
                    {
                        writer.WriteLine("=== Chunk [{0}, {1}]", this.chunkEngine.loadedChunks[i].X, this.chunkEngine.loadedChunks[i].Y);
                        for (int j = 0; j < this.chunkEngine.loadedChunks[i].partsEx2.Length; j++)
                        {
                            if (this.chunkEngine.loadedChunks[i].partsEx2[j] != null)
                            {
                                activeChunkParts++;
                                writer.WriteLine("---- ChunkPart [{0}]", j);
                                foreach (var buffer in this.chunkEngine.loadedChunks[i].partsEx2[j].buffers)
                                {
                                    buffers++;
                                    writer.WriteLine("+++++ Buffer [BlockID: {0}, VBufferContents: {1}, VBufferSize: {2}]", buffer.Key, buffer.Value.vbufferSize, buffer.Value.vbufferSize);
                                    vertices += buffer.Value.vbufferSize;
                                }
                            }
                        }
                    }
                    writer.WriteLine("Active chunk parts: {0}", activeChunkParts);
                    writer.WriteLine("# Buffers: {0}", buffers);
                    writer.WriteLine("# Vertices: {0}, (Memory usage: {1} MB)", vertices, (vertices * VertexPositionNormalTexture.VertexDeclaration.VertexStride) / (1024 * 1024));
                    //this.chunkEngine.loadedChunks[0].
                }
            }
            if (keyState.IsKeyDown(Keys.F) && oldState.IsKeyUp(Keys.F))
            {
                for (int i = 0; i < this.chunkEngine.loadedChunks.Count; i++)
                {
                    for (int j = 0; j < this.chunkEngine.loadedChunks[i].partsEx2.Length; j++)
                    {
                        if (this.chunkEngine.loadedChunks[i].partsEx2[j] != null)
                        {
                            this.chunkEngine.loadedChunks[i].partsEx2[j].needsUpdate = true;
                            this.chunkEngine.loadedChunks[i].partsEx2[j].Update();
                        }
                    }
                }
            }

            MouseState mouseState = Mouse.GetState();
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                Vector3 closePoint = new Vector3(mouseState.X, mouseState.Y, 0);
                Vector3 farPoint = new Vector3(mouseState.X, mouseState.Y, 10);
                closePoint = this.GraphicsDevice.Viewport.Unproject(closePoint, this.camera.Projection, this.camera.View, Matrix.Identity);
                farPoint = this.GraphicsDevice.Viewport.Unproject(farPoint, this.camera.Projection, this.camera.View, Matrix.Identity);

                mouseRay = new Ray(closePoint, closePoint - farPoint);
                mouseRay.Direction.Normalize();

                RayIntersectionResult? result;
                if (chunkEngine.CastRay(mouseRay, 100, out result))
                {
                    //Console.WriteLine(ccc++ + " " + result.ToString());
                    result.Value.ChunkPart.Parent.AddBlocks(new BlockDescriptorAdd[] {
                        new BlockDescriptorAdd() { Location = result.Value.Location, Type = (ushort)BlockType.Diamond}
                    });
                }
                else
                {
                    //Console.WriteLine(ccc++ + " Miss!");
                }
                //List<ChunkPartEx2> intersectedChunks = new List<ChunkPartEx2>(this.chunkEngine.loadedChunks.SelectMany(x => x.partsEx2).Where(x => x != null && x.isVisible));
                //intersectedChunks.Sort(this.CompareChunks);

                //BoundingBox[, ,] blockBounds = new BoundingBox[16, 16, 16];
                //float best = float.MaxValue;
                //Vector3? bestBlock = null;
                //int i;
                //for (i = 0; i < intersectedChunks.Count; i++)
                //{
                //    Vector3 chunkPartPosition = new Vector3(intersectedChunks[i].Parent.X * Chunk.ChunkWidth, intersectedChunks[i].Index * 16, intersectedChunks[i].Parent.Y * Chunk.ChunkWidth);
                //    BoundingBox[, ,] boxes = new BoundingBox[2, 2, 2];
                //    for (int x = 0; x < 2; x++)
                //    {
                //        for (int y = 0; y < 2; y++)
                //        {
                //            for (int z = 0; z < 2; z++)
                //            {
                //                boxes[x, y, z] = new BoundingBox(chunkPartPosition + new Vector3(8 * x, 8 * y, 8 * z), (chunkPartPosition + new Vector3(8 * x, 8 * y, 8 * z)) + new Vector3(8));

                //                float? intersection = boxes[x, y, z].Intersects(mouseRay);
                //                if (intersection.HasValue && intersection < best)
                //                {
                //                    BoundingBox[, ,] boxes2 = new BoundingBox[2, 2, 2];
                //                    for (int x2 = 0; x2 < 2; x2++)
                //                    {
                //                        for (int y2 = 0; y2 < 2; y2++)
                //                        {
                //                            for (int z2 = 0; z2 < 2; z2++)
                //                            {
                //                                boxes2[x2, y2, z2] = new BoundingBox(boxes[x, y, z].Min + new Vector3(4 * x2, 4 * y2, 4 * z2), (boxes[x, y, z].Min + new Vector3(4 * x2, 4 * y2, 4 * z2)) + new Vector3(4));

                //                                float? intersection2 = boxes2[x2, y2, z2].Intersects(mouseRay);
                //                                if (intersection2.HasValue && intersection2 < best)
                //                                {
                //                                    BoundingBox[, ,] boxes3 = new BoundingBox[2, 2, 2];
                //                                    for (int x3 = 0; x3 < 2; x3++)
                //                                    {
                //                                        for (int y3 = 0; y3 < 2; y3++)
                //                                        {
                //                                            for (int z3 = 0; z3 < 2; z3++)
                //                                            {
                //                                                boxes3[x3, y3, z3] = new BoundingBox(boxes2[x2, y2, z2].Min + new Vector3(2 * x3, 2 * y3, 2 * z3), (boxes2[x2, y2, z2].Min + new Vector3(2 * x3, 2 * y3, 2 * z3)) + new Vector3(2));

                //                                                float? intersection3 = boxes3[x3, y3, z3].Intersects(mouseRay);
                //                                                if (intersection3.HasValue && intersection3 < best)
                //                                                {
                //                                                    BoundingBox[, ,] boxes4 = new BoundingBox[2, 2, 2];
                //                                                    for (int x4 = 0; x4 < 2; x4++)
                //                                                    {
                //                                                        for (int y4 = 0; y4 < 2; y4++)
                //                                                        {
                //                                                            for (int z4 = 0; z4 < 2; z4++)
                //                                                            {
                //                                                                boxes4[x4, y4, z4] = new BoundingBox(boxes3[x3, y3, z3].Min + new Vector3(x4, y4, z4), (boxes3[x3, y3, z3].Min + new Vector3(x4, y4, z4)) + new Vector3(1));

                //                                                                float? intersection4 = boxes4[x4, y4, z4].Intersects(mouseRay);
                //                                                                if (intersection4.HasValue && intersection4 < best && intersectedChunks[i].blocks[(int)(boxes4[x4, y4, z4].Min - chunkPartPosition).X, (int)(boxes4[x4, y4, z4].Min - chunkPartPosition).Y, (int)(boxes4[x4, y4, z4].Min - chunkPartPosition).Z] != Block.AirID)
                //                                                                {
                //                                                                    best = intersection4.Value;
                //                                                                    bestBlock = boxes4[x4, y4, z4].Min - chunkPartPosition;
                //                                                                }
                //                                                            }
                //                                                        }
                //                                                    }
                //                                                }
                //                                            }
                //                                        }
                //                                    }
                //                                }
                //                            }
                //                        }
                //                    }
                //                }
                //            }   
                //        }
                //    }
                    
                //    if (bestBlock != null)
                //        break;
                //}
                //if (bestBlock != null)
                //{
                //    OutputDebugString(string.Format("{4}. Block ({0}) in chunk ({1}, {2}) ChunkPart {3} / 15\r\n", bestBlock, intersectedChunks[i].Parent.X, intersectedChunks[i].Parent.Y, intersectedChunks[i].Index, foofoo++));

                //    //Console.WriteLine(intersectedChunks[i].blockFaces[(int)bestBlock.Value.X, (int)bestBlock.Value.Y, (int)bestBlock.Value.Z]);
                //    if(keyState.IsKeyDown(Keys.LeftControl))
                //        intersectedChunks[i].BlocksPendingRemove.Enqueue(new Block(0) { X = (short)bestBlock.Value.X, Y = (short)bestBlock.Value.Y, Z = (short)bestBlock.Value.Z });
                //    if (keyState.IsKeyDown(Keys.LeftShift))
                //    {
                //        Vector3 chunkPartPosition = new Vector3(intersectedChunks[i].Parent.X * Chunk.ChunkWidth, intersectedChunks[i].Index * 16, intersectedChunks[i].Parent.Y * Chunk.ChunkWidth);
                //        Vector3 blockPosition = chunkPartPosition + bestBlock.Value;

                //        BoundingBox leftBlock = new BoundingBox(new Vector3(blockPosition.X - 1, blockPosition.Y, blockPosition.Z), Vector3.Zero);
                //        leftBlock.Max = leftBlock.Min + new Vector3(1);
                //        BoundingBox rightBlock = new BoundingBox(new Vector3(blockPosition.X + 1, blockPosition.Y, blockPosition.Z), Vector3.Zero);
                //        rightBlock.Max = rightBlock.Min + new Vector3(1);
                //        BoundingBox topBlock = new BoundingBox(new Vector3(blockPosition.X, blockPosition.Y + 1, blockPosition.Z), Vector3.Zero);
                //        topBlock.Max = topBlock.Min + new Vector3(1);
                //        BoundingBox bottomBlock = new BoundingBox(new Vector3(blockPosition.X, blockPosition.Y - 1, blockPosition.Z), Vector3.Zero);
                //        bottomBlock.Max = bottomBlock.Min + new Vector3(1);
                //        BoundingBox frontBlock = new BoundingBox(new Vector3(blockPosition.X, blockPosition.Y, blockPosition.Z + 1), Vector3.Zero);
                //        frontBlock.Max = frontBlock.Min + new Vector3(1);
                //        BoundingBox backBlock = new BoundingBox(new Vector3(blockPosition.X, blockPosition.Y, blockPosition.Z - 1), Vector3.Zero);
                //        backBlock.Max = backBlock.Min + new Vector3(1);

                //        Vector3? bestNeighbor = null;
                //        float bestNeighborDistance = float.MaxValue;
                //        float? distance;

                //        if (leftBlock.Min.X - chunkPartPosition.X >= 0 && leftBlock.Min.Y - chunkPartPosition.Y >= 0 && leftBlock.Min.Z - chunkPartPosition.Z >= 0 && leftBlock.Max.X - chunkPartPosition.X < 16 && leftBlock.Max.Y - chunkPartPosition.Y < 16 && leftBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = leftBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(leftBlock.Min.X - chunkPartPosition.X), (int)(leftBlock.Min.Y - chunkPartPosition.Y), (int)(leftBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = leftBlock.Min;
                //            }
                //        }

                //        if (rightBlock.Min.X - chunkPartPosition.X >= 0 && rightBlock.Min.Y - chunkPartPosition.Y >= 0 && rightBlock.Min.Z - chunkPartPosition.Z >= 0 && rightBlock.Max.X - chunkPartPosition.X < 16 && rightBlock.Max.Y - chunkPartPosition.Y < 16 && rightBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = rightBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(rightBlock.Min.X - chunkPartPosition.X), (int)(rightBlock.Min.Y - chunkPartPosition.Y), (int)(rightBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = rightBlock.Min;
                //            }
                //        }

                //        if (topBlock.Min.X - chunkPartPosition.X >= 0 && topBlock.Min.Y - chunkPartPosition.Y >= 0 && topBlock.Min.Z - chunkPartPosition.Z >= 0 && topBlock.Max.X - chunkPartPosition.X < 16 && topBlock.Max.Y - chunkPartPosition.Y < 16 && topBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = topBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(topBlock.Min.X - chunkPartPosition.X), (int)(topBlock.Min.Y - chunkPartPosition.Y), (int)(topBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = topBlock.Min;
                //            }
                //        }

                //        if (bottomBlock.Min.X - chunkPartPosition.X >= 0 && bottomBlock.Min.Y - chunkPartPosition.Y >= 0 && bottomBlock.Min.Z - chunkPartPosition.Z >= 0 && bottomBlock.Max.X - chunkPartPosition.X < 16 && bottomBlock.Max.Y - chunkPartPosition.Y < 16 && bottomBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = bottomBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(bottomBlock.Min.X - chunkPartPosition.X), (int)(bottomBlock.Min.Y - chunkPartPosition.Y), (int)(bottomBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = bottomBlock.Min;
                //            }
                //        }

                //        if (frontBlock.Min.X - chunkPartPosition.X >= 0 && frontBlock.Min.Y - chunkPartPosition.Y >= 0 && frontBlock.Min.Z - chunkPartPosition.Z >= 0 && frontBlock.Max.X - chunkPartPosition.X < 16 && frontBlock.Max.Y - chunkPartPosition.Y < 16 && frontBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = frontBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(frontBlock.Min.X - chunkPartPosition.X), (int)(frontBlock.Min.Y - chunkPartPosition.Y), (int)(frontBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = frontBlock.Min;
                //            }
                //        }

                //        if (backBlock.Min.X - chunkPartPosition.X >= 0 && backBlock.Min.Y - chunkPartPosition.Y >= 0 && backBlock.Min.Z - chunkPartPosition.Z >= 0 && backBlock.Max.X - chunkPartPosition.X < 16 && backBlock.Max.Y - chunkPartPosition.Y < 16 && backBlock.Max.Z - chunkPartPosition.Z < 16)
                //        {
                //            distance = backBlock.Intersects(mouseRay);
                //            if (distance.HasValue && distance < bestNeighborDistance && intersectedChunks[i].blocks[(int)(backBlock.Min.X - chunkPartPosition.X), (int)(backBlock.Min.Y - chunkPartPosition.Y), (int)(backBlock.Min.Z - chunkPartPosition.Z)] == Block.AirID)
                //            {
                //                bestNeighborDistance = distance.Value;
                //                bestNeighbor = backBlock.Min;
                //            }
                //        }

                //        if (bestNeighbor.HasValue)
                //        {
                //            BlockDescriptorAdd descriptor;
                //            descriptor.Type = (ushort)rand.Next(0, 2);
                //            descriptor.Location.X = (short)(bestNeighbor.Value.X - chunkPartPosition.X);
                //            descriptor.Location.Y = (short)(bestNeighbor.Value.Y - chunkPartPosition.Y);
                //            descriptor.Location.Z = (short)(bestNeighbor.Value.Z - chunkPartPosition.Z);
                //            intersectedChunks[i].BlocksPendingAdd.Enqueue(descriptor);
                //        }
                //            //intersectedChunks[i].BlocksPendingAdd.Add(new Block((ushort)rand.Next(0, 2)) { X = (short)(bestNeighbor.Value.X - chunkPartPosition.X), Y = (short)(bestNeighbor.Value.Y - chunkPartPosition.Y), Z = (short)(bestNeighbor.Value.Z - chunkPartPosition.Z) });
                //    }
                //}
            }

            oldMouseState = mouseState;
            

            if (keyState.IsKeyDown(Keys.Escape))
                this.Exit();

            if (keyState.IsKeyDown(Keys.D1) && oldState.IsKeyUp(Keys.D1))
                this.wireFrame = !this.wireFrame;

            if (keyState.IsKeyDown(Keys.LeftControl))
            {
                if (keyState.IsKeyDown(Keys.Enter) && oldState.IsKeyUp(Keys.Enter))
                {
                    this.CVarSystem.Get("fullscreen").SetBool(!this.graphics.IsFullScreen);
                    this.chunkEngine.WaitForEndOfChunkUpdate();
                    this.SetFullscreen(!this.graphics.IsFullScreen);

                    this.camera.AspectRatio = this.GraphicsDevice.Viewport.AspectRatio;
                }
            }

            if (keyState.IsKeyDown(Keys.P) && oldState.IsKeyUp(Keys.P))
                chunkEngine.onlyOne = !chunkEngine.onlyOne;
            if (keyState.IsKeyDown(Keys.O) /*&& oldState.IsKeyUp(Keys.O)*/)
            {
                Chunk c = chunkEngine.GetChunk(-1, 2);
                c.AddBlocks(new BlockDescriptorAdd[] { new BlockDescriptorAdd() { Location = new PointInt16() { X = 0, Y = (short)this.asdfgh++, Z = 0 }, Type = (ushort)BlockType.Diamond } });
            }

            body1.ApplyPendingForce(force * 10);

            oldState = keyState;
            
            // TODO: Add your update logic here
            //System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
            engine.Update(gameTime);
            //watch.Stop();
            //timePhysics = watch.ElapsedMilliseconds;
            //watch.Restart();
            //engine.Scene.Update();
            //watch.Stop();
            //timeOctreeRebuild = watch.ElapsedMilliseconds;

            camera.Target = this.body1.Position;
            camera.Position = this.body1.Position + new Vector3(0, 0, 10);
            player.Position = camera.Position;

            
            //Window.Title = body1.Position.ToString(); //body1.Velocity.ToString(); //gameTime.TotalGameTime.TotalSeconds.ToString(); // this.body1.Position.ToString();
            //this.chunkEngine.Update(gameTime, camera, this.body1.Position);

            matrixData = 0;
            verticeCount = 0;
            indicesCount = 0;
#if DEBUG_METRICS
            for (int i = 0; i < this.chunkEngine.loadedChunks.Count; i++)
            {
                if (!this.chunkEngine.loadedChunks[i].IsCompressed)
                {
                    for (int j = 0; j < this.chunkEngine.loadedChunks[i].partsEx2.Length; j++)
                    {
                        if (this.chunkEngine.loadedChunks[i].partsEx2[j] != null)
                        {
                            verticeCount += this.chunkEngine.loadedChunks[i].partsEx2[j].VertexCount;
                            indicesCount += this.chunkEngine.loadedChunks[i].partsEx2[j].IndexCount;
                        }
                    }
                }
                else
                {
                    matrixData += (ulong)this.chunkEngine.loadedChunks[i].compressionStream.Position;
                }
            }
#endif

            Window.Title = string.Format("{0},{1} - {2}", Math.Ceiling(this.player.Position.X / 16), Math.Ceiling(this.player.Position.Z / 16), this.player.Position);

            this.chunkEngine.Update(gameTime);
            this.camera.Update(gameTime);
            base.Update(gameTime);
        }
        int ccc = 0;
        int asdfgh = 0;
        ulong indicesCount = 0;
        ulong matrixData = 0;
        ulong verticeCount = 0;
        double secondCounter = 0;
        int frames;
        Color meh = Color.SkyBlue;
        int fps;
        Random rand = new Random();
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            frames++;
            secondCounter += gameTime.ElapsedGameTime.TotalSeconds;
            if (secondCounter > 1.0)
            {
                secondCounter -= 1.0;
                fps = frames;
                frames = 0;
            }
            GraphicsDevice.Clear(meh);

            this.effect.TextureEnabled = true;

            this.effect.View = this.camera.View;
            this.effect.Projection = this.camera.Projection;

            if (this.wireFrame)
                this.GraphicsDevice.RasterizerState = new RasterizerState() { FillMode = Microsoft.Xna.Framework.Graphics.FillMode.WireFrame, CullMode = CullMode.CullCounterClockwiseFace };
            else
                this.GraphicsDevice.RasterizerState = new RasterizerState() { FillMode = Microsoft.Xna.Framework.Graphics.FillMode.Solid, CullMode = CullMode.CullCounterClockwiseFace };
            
            System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();

            this.effect.EnableDefaultLighting();

            this.effect.SpecularColor = Color.Black.ToVector3();
            

            this.effect.CurrentTechnique.Passes[0].Apply();
            int visibleChunkParts = 0;
            chunkEngine.DrawChunks(gameTime, GraphicsDevice, this.effect, this.camera);
            if (this.wireFrame)
                this.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

            //frameCount++;
            watch.Stop();
            timeRender = watch.ElapsedMilliseconds;

            long timeTotal = timePhysics + timeRender + timeOctreeRebuild;
            spriteBatch.Begin();
            int cuboids = 0;
            spriteBatch.DrawString(this.font, string.Format("Physics: {0:P1} ({1} ms)\nOctree: {2:P1} ({3} ms)\nRender: {4:P1} ({5} ms)\nFPS: {6}\nCuboids: {7}\nVisible: {8}\nVertex memory usage: {9:0.00} MB\nMatrix memory usage: {10:0.00} MB\nLoaded chunks: {11} ({12}C / {13}UC)\nFrustum chunks: {14}\nSorted chunkparts: {15}\nVisible chunkparts: {16}",
                timePhysics / (double)timeTotal,
                timePhysics,
                timeOctreeRebuild / (double)timeTotal,
                timeOctreeRebuild,
                timeRender / (double)timeTotal,
                timeRender,
                fps,
                cuboids,
                visibleChunkParts,
                (((verticeCount * (ulong)PackedVertexPositionNormalTexture.VertexDeclaration.VertexStride)) + (indicesCount * sizeof(short))) / (float)(1024 * 1024),
                (matrixData) / (float)(1024 * 1024),
                this.chunkEngine.loadedChunks.Count + this.chunkEngine.compressedChunks.Count,
#if DEBUG_METRICS
                this.chunkEngine.compressedChunks.Count,
                this.chunkEngine.loadedChunks.Count,
                this.chunkEngine.FrustumChunks,
                this.chunkEngine.SortedChunkParts,
                this.chunkEngine.VisibleChunkParts
#else
                "N/A",
                "N/A",
                "N/A",
                "N/A",
                "N/A"
#endif
                ), new Vector2(5, 5), Color.White);
            spriteBatch.End();
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            base.Draw(gameTime);
        }

        private Vector3 lastPlayerPos;
        
    }


}
